package com.lin.service.pay;

import com.lin.common.constant.CommonConstant;
import com.lin.common.constant.CustomerServiceConstant;
import com.lin.common.error.CustomRuntimeException;
import com.lin.common.error.ErrorCode;
import com.lin.common.utils.BeanCopyUtils;
import com.lin.common.utils.DateUtils;
import com.lin.common.utils.RedisSerializableUtils;
import com.lin.config.SpringContextIniter;
import com.lin.controller.req.OrderListReqMsg;
import com.lin.controller.req.OrderPayReqMsg;
import com.lin.controller.res.OrderPageResMsg;
import com.lin.dao.OrdersMapper;
import com.lin.dao.PayOrderMapper;
import com.lin.dao.TranslateSetupMapper;
import com.lin.pay.PayService;
import com.lin.pay.ali.ALIPayConstant;
import com.lin.pay.paypal.PaypalConstant;
import com.lin.pay.wx.WXPayConstant;
import com.lin.pay.wx.sdk.WXPayConfig;
import com.lin.pay.wx.sdk.WXPayConstants;
import com.lin.pay.wx.sdk.WXPayUtil;
import com.lin.pay.wx.sdk.WxPayConfigImpl;
import com.lin.po.Orders;
import com.lin.po.PayOrder;
import com.lin.po.TranslateSetup;
import com.lin.service.orders.OrdersService;
import com.lin.service.translatesetup.TranslateSetupService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class OrderPayServiceImpl implements OrderPayService {
    private static final Logger log = LoggerFactory.getLogger(OrderPayServiceImpl.class);
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private PayOrderMapper payOrderMapper;
    @Autowired
    private TranslateSetupMapper translateSetupMapper;
    @Autowired
    private PayService payService;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    @Transactional
    public Map<String, String> unifiedOrder(OrderPayReqMsg<TranslateSetup> reqMsg) throws CustomRuntimeException, UnsupportedEncodingException {

        Long orderNo = reqMsg.getOrderNo();

        Orders order = ordersMapper.selectByPrimaryKey(orderNo);
        //第一次下单，则进入IF内，生成订单号，否则为关闭支付控件后重新下单。
        if (orderNo == null || order == null) {
            //生成订单号 1
            orderNo = Long.parseLong(RedisSerializableUtils.generateForLong(CommonConstant.DEFAULT_LENGTH));
            //构建订单内容
            order = buildOrder(orderNo, reqMsg.getCustomerId(), reqMsg);
            ordersMapper.insert(order);
            //插入翻译设置信息
            TranslateSetup ts = buildTranslateSetup(reqMsg.getT(), reqMsg.getCustomerId(), orderNo);
            translateSetupMapper.insertSelective(ts);
        }
        //支付订单新增
        Long payOrderNo = Long.parseLong(RedisSerializableUtils.generateForLong(CommonConstant.DEFAULT_LENGTH));
        Map req = payService.unifiedOrder(reqMsg.getTradChnl(), reqMsg.getOpenid(), reqMsg.getTrxAmt(),reqMsg.getCurrency(), orderNo, payOrderNo);
        return req;
    }

    @Override
    public OrderPageResMsg orderlist(OrderListReqMsg reqMsg) {
        Orders o = BeanCopyUtils.beanCopy(reqMsg, Orders.class);
        OrderPageResMsg opr = new OrderPageResMsg();
        opr.setRecords(ordersMapper.orderlist(o));
        opr.setTotal(ordersMapper.orderlistCount(o));
        return opr;
    }


    @Override
    @Transactional
    public String channelNotify(Map<String, String> map) throws Exception {
        final WXPayConfig pc = SpringContextIniter.getBean(WxPayConfigImpl.class);
        if (StringUtils.equals(map.get(WXPayConstant.RETURN_CODE), WXPayConstant.RETURN_CODE_SUCCESS)) {
            Long payOrderNo = Long.parseLong(map.get(WXPayConstant.OUT_TRADE_NO));
            log.info("orderno:{}", payOrderNo);
            RBucket<String> rb = redissonClient.getBucket(CustomerServiceConstant.ORDER_PREFIX + payOrderNo);
            //防止多次回调进入逻辑
            if (!StringUtils.isNotBlank(rb.get())) {
                rb.set(payOrderNo + "", CustomerServiceConstant.ORDER_PREFIX_TIMEOUT, TimeUnit.HOURS);
                if (WXPayUtil.isSignatureValid(map, pc.getKey(), map.get(WXPayConstant.SIGN_TYPE_FIELD) == WXPayConstants.MD5 ? WXPayConstants.SignType.MD5 : WXPayConstants.SignType.HMACSHA256)) {
                    PayOrder po = payOrderMapper.selectByPrimaryKey(payOrderNo);
                    if (po == null) {
                        log.error("error obj is null {}", po);
                        throw new CustomRuntimeException(ErrorCode.ORDER_NOT_EXISTS, ErrorCode.ORDER_NOT_EXISTS.getMessage());
                    }
                    po.setPayTime(map.get(WXPayConstant.TIME_END));
                    po.setUpdateTime(DateUtils.fullDateFormat());
                    po.setTrxStatus(PayOrder.TRX_STATUS_SUCCESS);
                    po.setTransactionId(map.get(WXPayConstant.TRANSACTION_ID));
                    payOrderMapper.updateByPrimaryKeySelective(po);
                    //通知完成后对原有订单进行状态更新；
                    OrdersService ordersService = SpringContextIniter.getBean(OrdersService.class);
                    ordersService.orderStatusUpdate(po.getOrderNo());
                    //通知完成后对订单下的翻译设置进行状态更新；
                    TranslateSetupService translateSetupService = SpringContextIniter.getBean(TranslateSetupService.class);
                    translateSetupService.updateStatus(po.getOrderNo());
                    return CustomerServiceConstant.WXSUCCESS;
                }
            }
        }
        return CustomerServiceConstant.WXFAIL;
    }

    private TranslateSetup buildTranslateSetup(TranslateSetup ts, Long customerId, Long orderNo) throws CustomRuntimeException {
        if (ts == null) {
            return ts;
        } else {
            ts.setId(Long.parseLong(RedisSerializableUtils.generateForLong(CommonConstant.DEFAULT_LENGTH)));
            ts.setCustomerId(customerId);
            ts.setOrderNo(orderNo);
            switch (ts.getLevelNo()) {
                case CustomerServiceConstant.LEVEL_ORDINARY:
                    ts.setTotalCount(CustomerServiceConstant.LEVEL_ORDINARY_NUM);
                    ts.setFreeCount(CustomerServiceConstant.LEVEL_ORDINARY_NUM);
                    break;
                case CustomerServiceConstant.LEVEL_BRONZE:
                    ts.setTotalCount(CustomerServiceConstant.LEVEL_BRONZE_NUM);
                    ts.setFreeCount(CustomerServiceConstant.LEVEL_BRONZE_NUM);
                    break;
                case CustomerServiceConstant.LEVEL_SILVER:
                    ts.setTotalCount(CustomerServiceConstant.LEVEL_SILVER_NUM);
                    ts.setFreeCount(CustomerServiceConstant.LEVEL_SILVER_NUM);
                    break;
                case CustomerServiceConstant.LEVEL_GOLD:
                    ts.setTotalCount(CustomerServiceConstant.LEVEL_GOLD_NUM);
                    ts.setFreeCount(CustomerServiceConstant.LEVEL_GOLD_NUM);
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + ts.getLevelNo());
            }
            ts.setStatus(TranslateSetup.STATUS_INIT);
            ts.setCreateTime(DateUtils.fullDateFormat());
            ts.setUpdateTime(DateUtils.fullDateFormat());
        }
        return ts;
    }

    private Orders buildOrder(Long orderNo, Long customerId, OrderPayReqMsg opr) {
        Orders o = new Orders();
        o.setCustomerId(customerId);
        o.setOrderNo(orderNo);
        o.setTrxStatus(Orders.TRX_STATUS_USERPAYING);
        o.setTrxAmt(opr.getTrxAmt());
        switch (opr.getTradChnl()) {
            case WXPayConstant.WEIXIN:
                o.setPayType(WXPayConstant.WX_UNIFIED);
                break;
            case ALIPayConstant.ALI:
                o.setPayType(ALIPayConstant.ALI_UNIFIED);
                break;
            case PaypalConstant.PAYPAL:
                o.setPayType(PaypalConstant.PAYPAL_UNIFIED);
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + opr.getTradChnl());
        }
        o.setTradChnl(opr.getTradChnl());
        o.setBody(opr.getBody());
        o.setCreateTime(DateUtils.fullDateFormat());
        o.setUpdateTime(DateUtils.fullDateFormat());
        o.setInstDate(DateUtils.dateFormat());
        o.setInstTime(DateUtils.timeFormat());
        o.setBody(opr.getRemark());
        return o;
    }


    /**
     * 支付订单构建.
     *
     * @param payOrderNo
     * @param orderNo
     * @param trxAmt
     * @return
     */
    private PayOrder buildPayOrder(Long payOrderNo, Long orderNo, BigDecimal trxAmt) {
        PayOrder o = new PayOrder();
        o.setPayOrderNo(payOrderNo);
        o.setTrxStatus(Orders.TRX_STATUS_NOTPAY);
        o.setTrxAmt(trxAmt);
        o.setPayType(WXPayConstant.WX_MICRO);
        o.setTradChnl(CustomerServiceConstant.TRAD_CHNL_WEIXIN);
        o.setBody(orderNo + "");
        o.setCreateTime(DateUtils.fullDateFormat());
        o.setUpdateTime(DateUtils.fullDateFormat());
        o.setInstDate(DateUtils.dateFormat());
        o.setInstTime(DateUtils.timeFormat());
        return o;
    }
}
